/**
 ******************************************************************************
 * @file    wifi_board.cc
 * @author  summDy
 * @version V1.0.0
 * @date    26-06-2025
 * @brief   Brief description of the file
 ******************************************************************************
 * @attention
 *
 ******************************************************************************
 */

/**
 * 这里发现一个问题：层级不清晰（依赖反转）的问题
 * EnterWifiConfigMode方法调用了应用层Application
 */

#include "wifi_board.h"

#include "application.h"
#include "assets/lang_config.h"
#include "display.h"
#include "font_awesome_symbols.h"
#include "settings.h"
#include "system_info.h"

#include <esp_http.h>
#include <esp_log.h>
#include <esp_mqtt.h>
#include <esp_udp.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <tcp_transport.h>
#include <tls_transport.h>
#include <web_socket.h>

#include <ssid_manager.h>
#include <wifi_configuration_ap.h>
#include <wifi_station.h>

static const char *TAG = "WifiBoard";

WifiBoard::WifiBoard() {
  Settings settings("wifi", true);
  wifi_config_mode_ = settings.GetInt("force_ap") == 1;
  if (wifi_config_mode_) {
    ESP_LOGI(TAG, "force_ap is set to 1, reset to 0");
    settings.SetInt("force_ap", 0);
  }
}

std::string WifiBoard::GetBoardType() { return "wifi"; }

/**
 * @brief 进入WiFi配置模式
 *
 * 1. 设置设备状态为WiFi配置中
 * 2. 初始化WiFi AP配置，设置语言和SSID前缀
 * 3. 启动WiFi AP服务
 * 4. 显示连接热点和访问Web服务器的提示信息
 * 5. 播报WiFi配置提示音
 * 6. 进入无限循环，定期打印内存使用情况，等待WiFi配置完成
 *
 * @note 此函数会阻塞执行，直到设备重置
 */
void WifiBoard::EnterWifiConfigMode() {
  auto &application = Application::GetInstance();
  application.SetDeviceState(kDeviceStateWifiConfiguring);

  auto &wifi_ap = WifiConfigurationAp::GetInstance();
  wifi_ap.SetLanguage(Lang::CODE);
  wifi_ap.SetSsidPrefix("Xiaozhi");
  wifi_ap.Start();

  // 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
  std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT;
  hint += wifi_ap.GetSsid();
  hint += Lang::Strings::ACCESS_VIA_BROWSER;
  hint += wifi_ap.GetWebServerUrl();
  hint += "\n\n";

  // 播报配置 WiFi 的提示
  application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_WIFICONFIG);

  // Wait forever until reset after configuration
  while (true) {
    int free_sram     = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
    int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
    ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);
    vTaskDelay(pdMS_TO_TICKS(10000));
  }
}

void WifiBoard::StartNetwork() {
  // User can press BOOT button while starting to enter WiFi configuration mode
  if (wifi_config_mode_) {
    EnterWifiConfigMode();
    return;
  }

  // If no WiFi SSID is configured, enter WiFi configuration mode
  auto &ssid_manager = SsidManager::GetInstance();
  auto  ssid_list    = ssid_manager.GetSsidList();
  if (ssid_list.empty()) {
    wifi_config_mode_ = true;
    EnterWifiConfigMode();
    return;
  }

  auto &wifi_station = WifiStation::GetInstance();
  wifi_station.OnScanBegin([this]() {
    auto display = Board::GetInstance().GetDisplay();
    display->ShowNotification(Lang::Strings::SCANNING_WIFI, 30000);
  });
  wifi_station.OnConnect([this](const std::string &ssid) {
    auto        display      = Board::GetInstance().GetDisplay();
    std::string notification = Lang::Strings::CONNECT_TO;
    notification += ssid;
    notification += "...";
    display->ShowNotification(notification.c_str(), 30000);
  });
  wifi_station.OnConnected([this](const std::string &ssid) {
    auto        display      = Board::GetInstance().GetDisplay();
    std::string notification = Lang::Strings::CONNECTED_TO;
    notification += ssid;
    display->ShowNotification(notification.c_str(), 30000);
  });
  wifi_station.Start();

  // 最多等待 60 秒（60,000 毫秒）以建立 Wi-Fi 连接。这是一个阻塞调用，它会暂停执行，直到连接建立或超时发生。
  // Try to connect to WiFi, if failed, launch the WiFi configuration AP
  if (!wifi_station.WaitForConnected(60 * 1000)) {
    wifi_station.Stop();
    wifi_config_mode_ = true;
    EnterWifiConfigMode();
    return;
  }
}

Http *WifiBoard::CreateHttp() { return new EspHttp(); }

WebSocket *WifiBoard::CreateWebSocket() {
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
  std::string url = CONFIG_WEBSOCKET_URL;
  if (url.find("wss://") == 0) {
    return new WebSocket(new TlsTransport());
  } else {
    return new WebSocket(new TcpTransport());
  }
#endif
  return nullptr;
}

Mqtt *WifiBoard::CreateMqtt() { return new EspMqtt(); }

Udp *WifiBoard::CreateUdp() { return new EspUdp(); }

const char *WifiBoard::GetNetworkStateIcon() {
  if (wifi_config_mode_) {
    return FONT_AWESOME_WIFI;
  }
  auto &wifi_station = WifiStation::GetInstance();
  if (!wifi_station.IsConnected()) {
    return FONT_AWESOME_WIFI_OFF;
  }
  int8_t rssi = wifi_station.GetRssi();
  if (rssi >= -60) {
    return FONT_AWESOME_WIFI;
  } else if (rssi >= -70) {
    return FONT_AWESOME_WIFI_FAIR;
  } else {
    return FONT_AWESOME_WIFI_WEAK;
  }
}

std::string WifiBoard::GetBoardJson() {
  // Set the board type for OTA
  auto       &wifi_station = WifiStation::GetInstance();
  std::string board_json   = std::string("{\"type\":\"" BOARD_TYPE "\",");
  board_json += "\"name\":\"" BOARD_NAME "\",";
  if (!wifi_config_mode_) {
    board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
    board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
    board_json += "\"channel\":" + std::to_string(wifi_station.GetChannel()) + ",";
    board_json += "\"ip\":\"" + wifi_station.GetIpAddress() + "\",";
  }
  board_json += "\"mac\":\"" + SystemInfo::GetMacAddress() + "\"}";
  return board_json;
}

void WifiBoard::SetPowerSaveMode(bool enabled) {
  auto &wifi_station = WifiStation::GetInstance();
  wifi_station.SetPowerSaveMode(enabled);
}

void WifiBoard::ResetWifiConfiguration() {
  // Set a flag and reboot the device to enter the network configuration mode
  {
    Settings settings("wifi", true);
    settings.SetInt("force_ap", 1);
  }
  GetDisplay()->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE);
  vTaskDelay(pdMS_TO_TICKS(1000));
  // Reboot the device
  esp_restart();
}
